home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Workspace / Locus / Source / Group.m < prev    next >
Text File  |  1995-06-12  |  22KB  |  1,128 lines

  1.  
  2. /*
  3.     Copyright 1993  Jeremy Slade.  All rights reserved.
  4. */
  5.  
  6. #import "Group.h"
  7.  
  8. #import "AtomList.h"
  9. #import "DynamicItemSpec.h"
  10. #import "DynamicItems.h"
  11. #import "Folder.h"
  12. #import "FolderController.h"
  13. #import "FolderViewer.h"
  14. #import "Globals.h"
  15. #import "GroupBrowser.h"
  16. #import "Inspector.h"
  17. #import "ItemCell.h"
  18. #import "MainController.h"
  19.  
  20. #import <string.h>
  21. #import <sys/file.h>
  22.  
  23.  
  24. @implementation Group
  25.  
  26.  
  27. // -------------------------------------------------------------------------
  28. //   Creating, intializing Methods
  29. // -------------------------------------------------------------------------
  30.  
  31.  
  32. + initialize
  33. /*
  34.     Set the class version number
  35. */
  36. {
  37.     [self setVersion:Group_VERSION];
  38.     return ( self );
  39. }
  40.  
  41.  
  42.  
  43. + new
  44. {
  45.     return ( [[Group alloc] init] );
  46. }
  47.  
  48.  
  49.  
  50. - init
  51. {
  52.     return ( [self initCount:0] );
  53. }
  54.  
  55.  
  56.  
  57. - initCount:(unsigned int)numSlots
  58. /*
  59.     Designated initializer.  Initialize list and set the default groupName
  60. */
  61. {
  62.     [super initCount:numSlots];
  63.     
  64.     [self setGroupName:"UNNAMED"];
  65.     [self setKeyEquivalent:0];
  66.     
  67.     tag = 0;    // Assigned by the Folder
  68.     
  69.     restrictTypes = NO; // Allow all files by default
  70.     
  71.     // Set default drawInfo
  72.     drawInfo.mode = IC_LARGE_BROWSE;
  73.     drawInfo.actualImage = YES;
  74.     drawInfo.lb_tridots = YES;
  75.     drawInfo.lb_nameMode = IC_FILEANDPATH;
  76.     drawInfo.lb_info1 = YES;
  77.     drawInfo.lb_info2 = NO;
  78.     drawInfo.sb_icon = YES;
  79.     drawInfo.sb_nameMode = IC_FILEANDPATH;
  80.     drawInfo.li_showName = YES;
  81.     drawInfo.si_showName = YES;
  82.  
  83.     filter = NULL;
  84.     defaultPath = NULL;
  85.     
  86.     typesList = [[AtomList alloc] initCount:0];
  87.     
  88.     dynamicItemSpecs = [[List alloc] initCount:0];
  89.     dynamicUpdateInterval = 0.0;    // Off by default
  90.     
  91.     return ( self );
  92. }
  93.  
  94.  
  95.  
  96. - free
  97. /*
  98.     Free the instance and any objects it allocates
  99. */
  100. {
  101.     if ( groupName ) NX_FREE ( groupName );
  102.     if ( filter ) NX_FREE ( filter );
  103.     if ( defaultPath ) NX_FREE ( defaultPath );
  104.     
  105.     [typesList free];
  106.     [dynamicItemSpecs free];
  107.     
  108.     return ( [super free] );
  109. }
  110.  
  111.  
  112.  
  113. // -------------------------------------------------------------------------
  114. //   Naming the Group
  115. // -------------------------------------------------------------------------
  116.  
  117.  
  118. - setGroupName:(const char *)aString
  119. /*
  120.     Set the group's name to the specified string
  121. */
  122. {
  123.     if ( groupName ) {
  124.         NX_FREE ( groupName );
  125.         [self setChanged:YES]; // Only 'changed' if it already had a name
  126.     }
  127.  
  128.     groupName = NXCopyStringBuffer ( aString );
  129.  
  130.     return ( self );
  131. }
  132.  
  133.  
  134.  
  135. - (const char *)groupName
  136. {
  137.     return ( groupName );
  138. }
  139.  
  140.  
  141.  
  142. // -------------------------------------------------------------------------
  143. //   Group Tag
  144. // -------------------------------------------------------------------------
  145.  
  146.  
  147. - setTag:(int)aTag
  148. /*
  149. Called by the Group's Folder when it is assigned a unique tag identifying it within this Folder
  150. */
  151. {
  152.     if ( aTag != tag ) {
  153.         int oldTag = tag;
  154.         tag = aTag;
  155.         if ( tag != oldTag ) [self setChanged:YES];
  156.     }
  157.  
  158.     return ( self );
  159. }
  160.  
  161.  
  162.  
  163. - (int)tag
  164. {
  165.     return ( tag );
  166. }
  167.  
  168.  
  169.  
  170. // -------------------------------------------------------------------------
  171. //   Setting general attributes
  172. // -------------------------------------------------------------------------
  173.  
  174.  
  175. - setKeyEquivalent:(unsigned short)charCode
  176. /*
  177.     Sets the key equivalent for selecting the group
  178. */
  179. {
  180.     if ( keyEquivalent != charCode ) {
  181.         keyEquivalent = charCode;
  182.         [self setChanged:YES];
  183.     }
  184.     
  185.     return ( self );
  186. }
  187.  
  188.  
  189.  
  190. - setDefaultPath:(const char *)fullPath
  191. /*
  192.     Sets the default path that this Group is associated with.  This path is the directory that the 'Add Items' panel will automatically be set to, and it is the path used for relative dynamicItems specifiers.
  193. */
  194. {
  195.     if ( defaultPath != fullPath || ( defaultPath && fullPath &&
  196.         strcmp ( defaultPath, fullPath ) ) ) {
  197.         
  198.         if ( defaultPath ) NX_FREE ( defaultPath );
  199.         defaultPath = fullPath ? NXCopyStringBuffer ( fullPath ) : NULL;
  200.         [self makeObjectsPerform:@selector(resetFileStr)];
  201.         [self setChanged:YES];
  202.     }
  203.     
  204.     return ( self );
  205. }
  206.  
  207.  
  208.  
  209. - setSortItems:(BOOL)flag
  210. /*
  211.     Sets whether the group will automatically sort itself when new items are added
  212. */
  213. {
  214.     if ( sortItems != flag ) {
  215.         sortItems = flag;
  216.         [self setChanged:YES];
  217.     }
  218.     
  219.     return ( self );
  220. }
  221.  
  222.  
  223.  
  224. - (unsigned short)keyEquivalent
  225. {
  226.     return ( keyEquivalent );
  227. }
  228.  
  229.  
  230.  
  231. - (const char *)defaultPath
  232. {
  233.     return ( defaultPath );
  234. }
  235.  
  236.  
  237.  
  238. - (BOOL)doesSortItems
  239. {
  240.     return ( sortItems );
  241. }
  242.  
  243.  
  244.  
  245. // -------------------------------------------------------------------------
  246. //   Setting drawing attributes
  247. // -------------------------------------------------------------------------
  248.  
  249.  
  250. - setDrawMode:(int)aMode
  251. {
  252.     int newMode;
  253.     
  254.     switch ( aMode ) {
  255.         case IC_LARGE_BROWSE:
  256.         case IC_SMALL_BROWSE:
  257.             newMode = aMode;
  258.             break;
  259.         case IC_LARGE_ICON:    // Not Implemented
  260.         case IC_SMALL_ICON:    // Not Implemented
  261.         default:
  262.             newMode = IC_LARGE_BROWSE;
  263.             break;
  264.     }
  265.     
  266.     if ( drawInfo.mode != newMode ) {
  267.         drawInfo.mode = newMode;
  268.         [folder setNeedsLoadBrowser:YES];
  269.         [self setChanged:YES];
  270.     }
  271.     
  272.     return ( self );
  273. }
  274.  
  275.  
  276.  
  277. - (int)drawMode
  278. {
  279.     return ( drawInfo.mode );
  280. }
  281.  
  282.  
  283.  
  284. - setDrawActualImage:(BOOL)flag
  285. {
  286.     if ( drawInfo.actualImage != flag ) {
  287.         drawInfo.actualImage = flag;
  288.         [self setChanged:YES];
  289.         [folder setNeedsLoadBrowser:YES];
  290.     }
  291.     return ( self );
  292. }
  293.  
  294.  
  295.  
  296. - (BOOL)doesDrawActualImage
  297. {
  298.     return ( drawInfo.actualImage );
  299. }
  300.  
  301.  
  302.  
  303. - setDrawTriDots:(BOOL)flag
  304. {
  305.     if ( drawInfo.lb_tridots != flag ) {
  306.         drawInfo.lb_tridots = flag;
  307.         [self setChanged:YES];
  308.         [folder setNeedsLoadBrowser:YES];
  309.     }
  310.     return ( self );
  311. }
  312.  
  313.  
  314.  
  315. - (BOOL)doesDrawTriDots
  316. {
  317.     return ( drawInfo.lb_tridots );
  318. }
  319.  
  320.  
  321.  
  322. - setDrawInfoLine1:(BOOL)flag
  323. {
  324.     if ( drawInfo.lb_info1 != flag ) {
  325.         drawInfo.lb_info1 = flag;
  326.         [self setChanged:YES];
  327.         [folder setNeedsLoadBrowser:YES];
  328.     }
  329.     return ( self );
  330. }
  331.  
  332.  
  333.  
  334. - (BOOL)doesDrawInfoLine1
  335. {
  336.     return ( drawInfo.lb_info1 );
  337. }
  338.  
  339.  
  340.  
  341. - setDrawInfoLine2:(BOOL)flag
  342. {
  343.     if ( drawInfo.lb_info2 != flag ) {
  344.         drawInfo.lb_info2 = flag;
  345.         [self setChanged:YES];
  346.         [folder setNeedsLoadBrowser:YES];
  347.     }
  348.     return ( self );
  349. }
  350.  
  351.  
  352.  
  353. - (BOOL)doesDrawInfoLine2
  354. {
  355.     return ( drawInfo.lb_info2 );
  356. }
  357.  
  358.  
  359.  
  360. - setLBNameMode:(int)mode
  361. {
  362.     int newMode = IC_NAME_UNKNOWN;
  363.     
  364.     switch ( mode ) {
  365.         case IC_FULLPATH:
  366.         case IC_FILEANDPATH:
  367.         case IC_FILEONLY:
  368.         case IC_RELATIVE:
  369.         case IC_NOPATH:
  370.             newMode = mode;
  371.             break;
  372.         case IC_NAME_UNKNOWN:
  373.         default:
  374.             newMode = IC_FILEANDPATH;
  375.             break;
  376.     }
  377.     
  378.     if ( drawInfo.lb_nameMode != newMode ) {
  379.         drawInfo.lb_nameMode = newMode;
  380.         [self makeObjectsPerform:@selector(resetFileStr)];
  381.         [folder setNeedsLoadBrowser:YES];
  382.         [self setChanged:YES];
  383.     }
  384.     
  385.     return ( self );
  386. }
  387.  
  388.  
  389.  
  390. - (int)lbNameMode
  391. {
  392.     return ( drawInfo.lb_nameMode );
  393. }
  394.  
  395.  
  396.  
  397. - setDrawSmallIcons:(BOOL)flag
  398. {
  399.     if ( drawInfo.sb_icon != flag ) {
  400.         drawInfo.sb_icon = flag;
  401.         [self setChanged:YES];
  402.         [folder setNeedsLoadBrowser:YES];
  403.     }
  404.     return ( self );
  405. }
  406.  
  407.  
  408.  
  409. - (BOOL)doesDrawSmallIcons
  410. {
  411.     return ( drawInfo.sb_icon );
  412. }
  413.  
  414.  
  415.  
  416. - setSBNameMode:(int)mode
  417. {
  418.     int newMode = IC_NAME_UNKNOWN;
  419.     
  420.     switch ( mode ) {
  421.         case IC_FULLPATH:
  422.         case IC_FILEANDPATH:
  423.         case IC_FILEONLY:
  424.         case IC_RELATIVE:
  425.         case IC_NOPATH:
  426.             newMode = mode;
  427.             break;
  428.         case IC_NAME_UNKNOWN:
  429.         default:
  430.             newMode = IC_FILEANDPATH;
  431.             break;
  432.     }
  433.     
  434.     if ( drawInfo.sb_nameMode != newMode ) {
  435.         drawInfo.sb_nameMode = newMode;
  436.         [self makeObjectsPerform:@selector(resetFileStr)];
  437.         [folder setNeedsLoadBrowser:YES];
  438.         [self setChanged:YES];
  439.     }
  440.     
  441.     return ( self );
  442. }
  443.  
  444.  
  445.  
  446. - (int)sbNameMode
  447. {
  448.     return ( drawInfo.sb_nameMode );
  449. }
  450.  
  451.  
  452.  
  453. - setLIDrawName:(BOOL)flag
  454. {
  455.     if ( drawInfo.li_showName != flag ) {
  456.         drawInfo.li_showName = flag;
  457.         [self setChanged:YES];
  458.         [folder setNeedsLoadBrowser:YES];
  459.     }
  460.     return ( self );
  461. }
  462.  
  463.  
  464.  
  465. - (BOOL)liDoesDrawName
  466. {
  467.     return ( drawInfo.li_showName );
  468. }
  469.  
  470.  
  471.  
  472. - setSIDrawName:(BOOL)flag
  473. {
  474.     if ( drawInfo.si_showName != flag ) {
  475.         drawInfo.si_showName = flag;
  476.         [self setChanged:YES];
  477.         [folder setNeedsLoadBrowser:YES];
  478.     }
  479.     return ( self );
  480. }
  481.  
  482.  
  483.  
  484. - (BOOL)siDoesDrawName
  485. {
  486.     return ( drawInfo.si_showName );
  487. }
  488.  
  489.  
  490.  
  491. - getDrawInfo:(DrawInfo *)info
  492. {
  493.     *info = drawInfo;
  494.     return ( self );
  495. }
  496.  
  497.  
  498.  
  499. // -------------------------------------------------------------------------
  500. //   Allowed File Types
  501. // -------------------------------------------------------------------------
  502.  
  503.  
  504. - addAllowedType:(const char *)anExt
  505. /*
  506.     Add a new file type (e.g. .h, .m, .snd) to the list of allowed types for this group
  507. */
  508. {
  509.     NXAtom a = NXUniqueString ( anExt );
  510.     if ( [typesList indexOfAtom:a] == NX_NOT_IN_LIST  ) {
  511.         [typesList addAtomAlphabetically:a];
  512.         restrictTypes = YES; // Enable restrictions
  513.         [self setChanged:YES];
  514.     }
  515.     return ( self );
  516. }
  517.  
  518.  
  519.  
  520. - removeAllowedType:(const char *)anExt
  521. /*
  522.     Remove the specified type from the typesList
  523. */
  524. {
  525.     if ( [typesList indexOfAtom:NXUniqueString(anExt)] != NX_NOT_IN_LIST ) {
  526.         [typesList removeAtom:NXUniqueString(anExt)];
  527.         [self setChanged:YES];
  528.     }
  529.     return ( self );
  530. }
  531.  
  532.  
  533.  
  534. - setRestrictTypes:(BOOL)flag
  535. /*
  536.     Set the restrictTypes flag
  537. */
  538. {
  539.     if ( restrictTypes != flag ) {
  540.         restrictTypes = flag;
  541.         [self setChanged:YES];
  542.     }
  543.     return ( self );
  544. }
  545.  
  546.  
  547.  
  548. - (const char **)allowedTypes
  549. /*
  550.     Returns a NULL-terminated array of strings containing the allowed file types for this group.  This array is rebuilt each time this method is called.  The caller shouldn't keep the returned array around, as it will be freed
  551. */
  552. {
  553.     static const char **allowedTypes = NULL;
  554.     int i, count = 0;
  555.     
  556.     if ( allowedTypes ) { // Free previously allocated memory
  557.         NX_FREE ( allowedTypes );
  558.         allowedTypes = NULL;
  559.     }
  560.     
  561.     // Allocate memory for array
  562.     // Array must be big enough for all types + NULL pointer
  563.     if ( !NX_MALLOC ( allowedTypes, char *, [typesList count]+1 ) ) {
  564.         [self debug:10 :"Unable to allocate allowedTypes array!\n"];
  565.         return ( NULL );
  566.     }
  567.     
  568.     // Add all the types to the array
  569.     count = [typesList count];
  570.     for ( i=0; i<count; i++ ) {
  571.         allowedTypes[i] = [typesList atomAt:i];
  572.     }
  573.     
  574.     // Add NULL pointer at end of array
  575.     allowedTypes[count] = NULL;
  576.     return ( allowedTypes );
  577. }
  578.  
  579.  
  580.  
  581. - (BOOL)doesRestrictTypes
  582. /*
  583.     Returns YES if file type restrictions are currently active
  584. */
  585. {
  586.     return ( (!restrictTypes || ![typesList count]) ? NO : YES );
  587. }
  588.  
  589.  
  590.  
  591. - (BOOL)isAllowedType:(const char *)path
  592. /*
  593.     Checks if path is one of the types in the typesList
  594. */
  595. {
  596.     const char *ext;
  597.     NXAtom atom;
  598.     
  599.     if ( ![self doesRestrictTypes] ) // No Restrictions, all types allowed
  600.         return ( YES );
  601.         
  602.     ext = rindex ( path, '.' ); // Find last '.'
  603.     if ( ext ) ext++;
  604.         else ext = path;
  605.     atom = NXUniqueString ( ext );
  606.     
  607.     return ( [typesList indexOfAtom:atom] != NX_NOT_IN_LIST );
  608. }
  609.  
  610.  
  611.  
  612. // -------------------------------------------------------------------------
  613. //   Adding items to the group
  614. // -------------------------------------------------------------------------
  615.  
  616.  
  617. - addItem:(const char *)path
  618. /*
  619.     Add the item to the group if it doesn't already exists there, and if it is an allowed type
  620. */
  621. {
  622.     id    newItem;
  623.  
  624.     if ( [self itemExists:path] || ![self isAllowedType:path] )
  625.         return ( nil );
  626.  
  627.     if ( (newItem = [self itemMatching:path]) ) {
  628.         switch ( NXRunAlertPanel ( "Alert",
  629.             "There is already an item called %s in this group.  Do you want to replace the existing one?",
  630.             "Replace", "Add anyway", "Cancel", [newItem filename] ) ) {
  631.             case NX_ALERTDEFAULT:
  632.                 // Replace the existing item with the new path
  633.                 [newItem setPath:path];
  634.                 return ( self );
  635.                 
  636.             case NX_ALERTOTHER:
  637.             default:
  638.                 // Cancel
  639.                 return ( nil );
  640.         }
  641.     }
  642.     
  643.     newItem = [[ItemCell alloc] initPath:path];
  644.     [newItem setGroup:self];
  645.  
  646.     [self addObject:newItem];
  647.     [self setChanged:YES];
  648.     
  649.     if ( [folder currentGroup] == self ) [folder setNeedsLoadBrowser:YES];
  650.     return ( self );
  651. }
  652.  
  653.  
  654.  
  655. - (BOOL)itemExists:(const char *)path
  656. /*
  657.     checks all the items in the group for one that matches path
  658. */
  659. {
  660.     int i, count;
  661.     
  662.     count=[self count];
  663.     for ( i=0; i<count; i++ )
  664.         if ( !strcmp ( [[self objectAt:i] path], path ) )
  665.             return ( YES ); // Found a match
  666.             
  667.     return ( NO );
  668. }
  669.  
  670.  
  671.  
  672. - itemMatching:(const char *)path
  673. /*
  674.     Finds and returns the item (if it exists) that has the same filename as path--just the last component of the path.
  675. */
  676. {
  677.     const char *filename;
  678.     int i, count;
  679.     
  680.     filename = rindex ( path, '/' );
  681.     if ( filename ) filename++; else filename = path;
  682.     
  683.     count = [self count];
  684.     for ( i=0; i<count; i++ ) {
  685.         if ( !strcmp ( [[self objectAt:i] filename], filename ) )
  686.             return ( [self objectAt:i] ); // Found a match
  687.     }
  688.     
  689.     return ( nil );
  690. }
  691.  
  692.  
  693.  
  694. - cleanUp:sender
  695. /*
  696.     Filter items in group, removing all that aren't currently allowed types
  697. */
  698. {
  699.     int i, count, selCount;
  700.     BOOL oldVerify = verifyActions;
  701.     id item;
  702.     
  703.     if ( ![self doesRestrictTypes] )
  704.         return ( self );
  705.     
  706.     // Select all items to be removed
  707.     count = [self count];
  708.     selCount = 0;
  709.     for ( i=0; i<count; i++ ) {
  710.         if ( ![self isAllowedType:[(item=[self objectAt:i]) path]]
  711.             || access ( [item path], F_OK ) == -1 ) {
  712.             // Item will be tagged for removal if it is not an allowed
  713.             // type or if the file it references does not exist.
  714.             [item setState:1]; // Select for removal
  715.             selCount++;
  716.         } else
  717.             [item setState:0]; // Allowed type, won't be removed
  718.         [[[folder viewer] browser] scrollRowToVisible:i];
  719.         NXPing();
  720.     }
  721.     
  722.     if ( !verifyActions || NXRunAlertPanel ( "Clean Up",
  723.         "Remove %i restricted items from the group %s ?",
  724.         "Remove", "Cancel", NULL, selCount, groupName ) == NX_ALERTDEFAULT ) {
  725.         // Remove the selected items by calling removeSelectedItems:
  726.         verifyActions = NO; // Temporarily disable verifcation
  727.         [self removeSelectedItems:self];
  728.         verifyActions = oldVerify; // Restore old state
  729.     }
  730.     
  731.     return ( self );
  732. }
  733.  
  734.  
  735.  
  736. // -------------------------------------------------------------------------
  737. //   Sorting Items
  738. // -------------------------------------------------------------------------
  739.  
  740.  
  741. static int compareItems ( ItemCell **item1, ItemCell **item2 )
  742. /*
  743.     Compares the items for sorting, based on their filenames ( the last component of the item's path ).  Comparison is NOT case-sensitive.  This functions is used by the qsort() routine.
  744. */
  745. {
  746.     int compare;
  747.     compare = NXOrderStrings( [*item1 filename], [*item2 filename], NO, -1, NULL );
  748.     return ( compare );
  749. }
  750.  
  751.  
  752.  
  753. - sortItems
  754. /*
  755.     Sort the items in currentGroup alphabetically by filename ( last component of path ).  Uses qsort() to do this.  Qsort() calls our comparison function, compareItems(), and the directly reorders them in the dataPtr array.
  756. */
  757. {    
  758.     if ( [self doesSortItems] || needsSort  ) {
  759.         // Qsort()'s parameters are:
  760.         //    1. pointer to beginning of data -- in this case, dataPtr
  761.         //    2. number of elements to be sorted -- numElements
  762.         //    3. size of the element -- sizeof ( id * )
  763.         //    4. the comparison function -- compareItems() 
  764.         qsort ( dataPtr, numElements, sizeof(id *), compareItems );
  765.         
  766.         [self setChanged:YES];
  767.         [folder setNeedsLoadBrowser:YES];
  768.     }
  769.     
  770.     return ( self );
  771. }
  772.  
  773.  
  774.  
  775. - explicitSortItems:sender
  776. /*
  777.     Sort the items in the current group, overriding the setting of the sortItems preference
  778. */
  779. {
  780.     needsSort = YES;
  781.     [self sortItems];
  782.     return ( self );
  783. }
  784.  
  785.  
  786.  
  787. // -------------------------------------------------------------------------
  788. //   Making Selection
  789. // -------------------------------------------------------------------------
  790.  
  791.  
  792. - selectItem:sender
  793. /*
  794.     Called when an item is selected in browser
  795. */
  796. {
  797.     if ( [inspector inspectorMode] == INSPECT_ITEM )
  798.         [inspector inspect:[[[folder viewer] browser] selection]];
  799.     return ( self );
  800. }
  801.  
  802.  
  803.  
  804. - selectAll:sender
  805. {
  806.     [[[folder viewer] browser] selectAll:self];
  807.     [folder showSelf:self];
  808.     
  809.     if ( [inspector inspectorMode] == INSPECT_ITEM )
  810.         [inspector inspect:[[[folder viewer] browser] selection]];
  811.     return ( self );
  812. }
  813.  
  814.  
  815.  
  816. - selection
  817. {
  818.     return ( [[[folder viewer] browser] selection] );
  819. }
  820.  
  821.  
  822. - selectionList
  823. {
  824.     return ( [[[folder viewer] browser] selectionList] );
  825. }
  826.  
  827.  
  828. - (int)selectionCount
  829. {
  830.     return ( [[[folder viewer] browser] selectionCount] );
  831. }
  832.  
  833.  
  834.  
  835. // -------------------------------------------------------------------------
  836. //   Acting on Items
  837. // -------------------------------------------------------------------------
  838.  
  839. - launchSelectedItems:sender
  840. {
  841.     int i, count;
  842.     char *s;
  843.     
  844.     for ( i=0, count=[self count]; i<count; i++ ) {
  845.         if ( [[self objectAt:i] state] ) {
  846.             // Check if the item is a Folder
  847.             //// FIX: Item should have method to do this ( do the OOP thing )
  848.             s = rindex ( [[self objectAt:i] filename], '.' );
  849.             if ( s ) s++;
  850.                 else s = (char *)[[self objectAt:i] filename];
  851.             if ( !strcmp ( s, FOLDER_EXT ) ) {
  852.                 [folderController openFolder:[[self objectAt:i] path]];
  853.             } else {
  854.                 [NXApp deactivateSelf];
  855.                 [[self objectAt:i] launch];
  856.             }
  857.         }
  858.     }
  859.     
  860.     [folder showSelf:self];
  861.     return ( self );
  862. }
  863.  
  864.  
  865. - removeSelectedItems:sender
  866. /*
  867.     Remove all the selected items from currentGroup
  868. */
  869. {
  870.     int i, count;
  871.     BOOL removedSomething = NO;
  872.     
  873.     for ( count=[self count], i=count-1; i>=0; i-- ) {
  874.         if ( [[self objectAt:i] state] ) {
  875.             // Remove the item
  876.             [[self removeObjectAt:i] free];
  877.             removedSomething = YES;
  878.         }
  879.     }
  880.     
  881.     if ( removedSomething ) {
  882.         [folder setNeedsLoadBrowser:YES];
  883.         [self setChanged:YES];    
  884.         return ( self );
  885.     } else
  886.         return ( nil );
  887. }
  888.  
  889.  
  890. //// FIX: Change to copySelection to Pbaord.
  891. //// Write selection as NXFilenamePboardType
  892. - copyItemToPboard:sender
  893. /*
  894.     Copy the path of the selected item into the pasteboard
  895. */
  896. {
  897.     id    pboard = [Pasteboard newName:NXGeneralPboard];
  898.     const char *const types[2] = { NXAsciiPboardType, NULL };
  899.     id item;
  900.     
  901.     if ( !(item = [[[folder viewer] browser] selectedCell]) )
  902.         return ( nil );
  903.  
  904.     [pboard declareTypes:types num:1 owner:nil];
  905.     [pboard writeType:NXAsciiPboardType
  906.         data:[item path]
  907.         length:strlen ( [item path] )];
  908.         
  909.     return ( self );
  910. }
  911.  
  912.  
  913.  
  914. // -------------------------------------------------------------------------
  915. //   Folder
  916. // -------------------------------------------------------------------------
  917.  
  918.  
  919. - setFolder:aFolder
  920. {
  921.     folder = aFolder;
  922.     [self setTag:[folder tagFor:self]];
  923.     return ( self );
  924. }
  925.  
  926.  
  927.  
  928. - folder
  929. {
  930.     return ( folder );
  931. }
  932.  
  933.  
  934.  
  935. - setChanged:(BOOL)flag
  936. {
  937.     isChanged = flag;
  938.     
  939.     if ( autoSave && isChanged ) {
  940.         [self writeSelf];
  941.         isChanged = NO;
  942.     }
  943.         
  944.     if ( isChanged ) [folder groupChanged:self];
  945.         else [folder groupSaved:self];
  946.     
  947.     return ( self );
  948. }
  949.  
  950.  
  951.  
  952. - (BOOL)isChanged
  953. {
  954.     return ( [folder isChanged] );
  955. }
  956.  
  957.  
  958.  
  959. - setNeedsShow:(BOOL)flag
  960. {
  961.     return ( [folder setNeedsShow:flag] );
  962. }
  963.  
  964.  
  965. - (BOOL)needsShow
  966. {
  967.     return ( [folder needsShow] );
  968. }
  969.  
  970.  
  971.  
  972. // -------------------------------------------------------------------------
  973. //   Archiving
  974. // -------------------------------------------------------------------------
  975.  
  976.  
  977. - awake
  978. {
  979.     [super awake];
  980.     
  981.     // Make sure all the items' group is set to self
  982.     [self makeObjectsPerform:@selector(setGroup:) with:self];
  983.  
  984.     // Start dynamic updating
  985.     [self startDynamicUpdate];
  986.  
  987.     return ( self );
  988. }
  989.  
  990.  
  991.  
  992. - read:(NXTypedStream *)stream
  993. {
  994.     unsigned int count, i;
  995.     int versionNumber;
  996.     
  997.     [super read:stream];
  998.     
  999.     // Read versionNumber
  1000.     versionNumber = NXTypedStreamClassVersion ( stream, [[self class] name] );
  1001.     
  1002.     if ( versionNumber <= Group_VERSION ) { // Up thru current version
  1003.  
  1004.         // Unarchive all of the items in the list -- see write: for details 
  1005.         // Read the number of items in the list and set the capacity
  1006.         // of the array
  1007.         NXReadType ( stream, "i", &count );
  1008.         [self setAvailableCapacity:count];
  1009.         numElements = count;
  1010.         for ( i=0; i<count; i++ ) dataPtr[i] = NXReadObject ( stream );
  1011.  
  1012.         NXReadTypes ( stream, "*iscc",
  1013.             &groupName,
  1014.             &tag,
  1015.             &keyEquivalent,
  1016.             &restrictTypes, &sortItems );
  1017.         NXReadTypes ( stream, DrawInfoTypeString, &drawInfo );
  1018.         NXReadTypes ( stream, "**f",
  1019.             &filter,
  1020.             &defaultPath,
  1021.             &dynamicUpdateInterval );
  1022.         typesList = NXReadObject ( stream );
  1023.         dynamicItemSpecs = NXReadObject ( stream );
  1024.     }
  1025.     
  1026.     else {    // Unrecognized archive version
  1027.         [self errMsg:"Group: unrecognized version %i of archived object!\n",
  1028.             versionNumber];
  1029.     }
  1030.     
  1031.     return ( self );
  1032. }
  1033.  
  1034.  
  1035.  
  1036. - write:(NXTypedStream *)stream
  1037. /*
  1038.     Overrides standard archiving behavior of List to deal with Dynamic Items.  We must make sure that the archived count is the number of static items (non-dynamic), since DynamicItems don't get archived.
  1039. */
  1040. {
  1041.     unsigned int i, count;
  1042.  
  1043.     // First, set numElements to 0 to make [super write:] think there is nothing to write
  1044.     count = numElements;
  1045.     numElements = 0;
  1046.     
  1047.     // Now let super do it's thing....
  1048.     [super write:stream];
  1049.     
  1050.     // Restore numElements
  1051.     numElements = count;
  1052.     
  1053.     // Archive all of the items ourselves
  1054.     count -= [self countDynamicItems]; // actual num. of items being archived
  1055.     NXWriteType ( stream, "i", &count );
  1056.     for ( i=0; i<numElements; i++ )
  1057.         if ( ![dataPtr[i] isDynamic] ) NXWriteObject ( stream, dataPtr[i] );
  1058.  
  1059.     // Write our instance variables
  1060.     NXWriteTypes ( stream, "*iscc",
  1061.         &groupName,
  1062.         &tag,
  1063.         &keyEquivalent,
  1064.         &restrictTypes, &sortItems );
  1065.     NXWriteTypes ( stream, DrawInfoTypeString, &drawInfo );
  1066.     NXWriteTypes ( stream, "**f",
  1067.         &filter,
  1068.         &defaultPath,
  1069.         &dynamicUpdateInterval );
  1070.     NXWriteObject ( stream, typesList );
  1071.     NXWriteObject ( stream, dynamicItemSpecs );
  1072.         
  1073.     return ( self );
  1074. }
  1075.  
  1076.  
  1077.  
  1078. - writeSelf
  1079. /*
  1080.     Archive self to a typed stream
  1081. */
  1082. {
  1083.     NXTypedStream *stream;
  1084.     char path[MAXPATHLEN+1];
  1085.     
  1086.     if ( ![folder filename] )
  1087.         return ( self );
  1088.         
  1089.     strcpy ( path, [self getGroupFilename] );
  1090.     
  1091.     if ( !(stream = NXOpenTypedStreamForFile ( path, NX_WRITEONLY )) ) {
  1092.         [self errMsg:"Group: unable to create output stream to archive.\n"];
  1093.         return ( self ); // Unable to open typed stream
  1094.     }
  1095.         
  1096.     NXWriteRootObject ( stream, self );
  1097.     NXCloseTypedStream ( stream );
  1098.     
  1099.     [self setChanged:NO];
  1100.     return ( self );
  1101. }
  1102.  
  1103.  
  1104.  
  1105. - (const char *)getGroupFilename
  1106. /*
  1107.     Return the filename that this group will archive to/unarchive from
  1108. */
  1109. {
  1110.     static char path[MAXPATHLEN+1] = "";
  1111.     
  1112.     if ( !folder ) {
  1113.         [self errMsg:"Group: Unowned group %s!\n", groupName];
  1114.         return ( NULL );
  1115.     }
  1116.     
  1117.     if ( !tag ) [self setTag:[folder tagFor:self]];
  1118.     
  1119.     sprintf ( path, "%s/Group%05i.%s", [folder filename], tag, GROUP_EXT );
  1120.     return ( path );
  1121. }
  1122.  
  1123.  
  1124.  
  1125. @end
  1126.  
  1127.  
  1128.